package problem;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;

import core.ObjectiveTarget;
import core.Problem;
//TODO Przerobić
import core.Result;

public class CAPM extends Problem {
	
	protected Comparator<Object> comparator;
	private double predictions[];
	private String names[];
	private double covariances[][];
	
	public CAPM() {
		this.name = "CAPM";
		this.restLowerBounds = 0;
		this.restUpperBounds = 1;
		this.firstLowerBounds = new double[0];
		this.firstUpperBounds = new double[0];
		this.objectiveTargets = new ObjectiveTarget[2];
		this.objectiveTargets[0] = ObjectiveTarget.MINIMIZE;
		this.objectiveTargets[1] = ObjectiveTarget.MAXIMIZE;
		this.predictions = new double[15];
		this.covariances = new double[15][15];
		int i=0;
		int j=0;
		this.predictions[i] = 5.5;
		j=0;
		this.covariances[j][i]=.0;
		this.predictions[i] = 0.63;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=3.76;
		i++;
		this.predictions[i] = .18;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=0.3;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=0.31;
		i++;
		this.predictions[i] = .41;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.71;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.28;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.66;
		i++;
		this.predictions[i] = -.03;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.05;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.02;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.04;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.04;
		i++;
		this.predictions[i] = .17;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.23;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.79;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.72;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.02;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.68;
		i++;
		this.predictions[i] = .3;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.88;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.03;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.26;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.13;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.16;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.85;
		i++;
		this.predictions[i] = .98;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=3.3;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.09;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.59;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.01;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.82;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.97;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=3.19;
		i++;
		this.predictions[i] = .04;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.2;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.05;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.01;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.02;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.18;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.12;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.19;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.07;
		i++;
		this.predictions[i] = -.11;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.33;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.25;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.4;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.08;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.03;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.32;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.97;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.11;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.55;
		i++;
		this.predictions[i] = .77;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.05;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.11;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.69;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.08;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.54;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.07;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.05;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.16;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.06;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.98;
		i++;
		this.predictions[i] = .42;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=3.74;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.22;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.51;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.11;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.95;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.64;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=3.17;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.15;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.38;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.72;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=3.92;
		i++;
		this.predictions[i] = 1.36;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.45;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.18;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.8;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.08;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.04;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.1;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.54;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.23;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.33;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.25;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.01;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=2.79;
		i++;
		this.predictions[i] = 2.06;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.01;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.08;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.13;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.14;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.33;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.63;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.14;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.15;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.26;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.7;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.14;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.46;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=1.73;
		i++;
		this.predictions[i] = 0.16;
		j=0;
		this.covariances[j][i]=this.covariances[i][j]=.0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.17;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.03;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.02;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=0;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.06;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.01;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.16;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.01;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.12;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=-.02;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.18;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.03;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.15;
		j++;
		this.covariances[j][i]=this.covariances[i][j]=.07;
		this.comparator = new Comparator<Object>() {

			@Override
			public int compare(Object o1, Object o2) {
				double d1 = ((double[])o1)[0];
				double d2 = ((double[])o2)[0];
				if(d2>d1) return -1;
				if(d2<d1) return 1;
				return 0;
			}
			
		};
	}

	@Override
	public double[] calculate(double[] genotype) {
		double predicted_return_rate = 0.;
		double predicted_risk = 0.;
		try {
			int i=0;
			while(true) {
				predicted_return_rate+=genotype[i]*this.predictions[i];
				try {
					int j=0;
					while(true) {
						predicted_risk+=genotype[i]*genotype[j]*this.covariances[i][j];
						j++;
					}
				} catch(Exception e) {}
				i++;
			}
		}catch(Exception e) {}
		double[] result = new double[2];
		result[0] = predicted_return_rate;
		result[1] = predicted_risk;
		return result;
	}
	
	@Override
	public String getOptimalSolution() {
		return null;
	}

	@Override
	public double getHVR(Set<Result> results) {
		return 1;
	}

	@Override
	public String performAnalysis(Double capital, Integer analysisNumber, Set<Result> results) {
		//result[0] = predicted_return_rate;
		//result[1] = predicted_risk;
		String ret = new String();

		DecimalFormat df = new DecimalFormat("0.00");

		Result [] resultSortedByRisk =  results.toArray(new Result[results.size()]);
		int [][] shares = new int [analysisNumber+1] [resultSortedByRisk[0].genotype.length];
		double capitalRest = capital;
		Arrays.sort(resultSortedByRisk, new Comparator<Result>(){
			//compare only predicted risk
			@Override
			public int compare(Result arg0, Result arg1) {
				double diff = arg1.result[1] - arg0.result[1];
				if (diff > 0)
					return -1;
				if (diff < 0)
					return 1;
				return 0;

			}}
		);

		int i = 0, j = 0, jj = 0;
		int incj = resultSortedByRisk.length/analysisNumber;
		double tmp = 0, tmpvalue = 0;

		ret += ("Analysis for the capital "+df.format(capital)+"\n");
		
		while (jj<analysisNumber){ //loop - all analysis
			double risk = resultSortedByRisk[j].result[1] ;
			double returnRate = resultSortedByRisk[j].result[0] ;
			double [] genotype = resultSortedByRisk[j].genotype; 
			double genoSum = 0;
			capitalRest = capital;
			int num = 0;

			//przewidywana stopa zwrotu i ryzyka
			ret += ((jj+1) + ". Theoretical predicted risk: " + df.format(risk) + 
					", return rate: " + df.format(returnRate)+
			"\n  No\tname\t\t  %\tto buy\tvalue of one");
			
			//sumujemy genotyp
			try {
				i = 0;
				while (true){
					genoSum += genotype[i];
					i++;
				}
				
			}catch (IndexOutOfBoundsException iex){
				
			}
			try {
				i = 0;
				while (true){
					//wypisanie nazwy akcji, obliczenie ilosci (ca�kowitej) do zakupu
					//TODO obliczenie przewidywanego zysku przy takim sk�adzie portfela
					tmp = (genotype[i] / genoSum) * capital;
					num = 0;
					//CONST
					tmpvalue = 1;
					tmp -= tmpvalue;
					while (tmp > 0.0){
						num++;
						tmp -= tmpvalue;
					}
					shares[jj][i] = num;
					capitalRest -= (num * tmpvalue);
					ret += ("\n  "+ (i+1) +".\t" 
							+ String.format("%-"+12+"s", names[i]) +"\t" 
							+ String.format("%"+6+"s",df.format((genotype[i] / genoSum)*100))+ "\t"
							+ String.format("%"+6+"s",num)+"\t"
							+ String.format("%"+6+"s",df.format(tmpvalue))); 
					i++;
				}
			} catch (IndexOutOfBoundsException iex){
				
			}
			//incrementing j
			j += incj;
			jj++;
			ret += ("\n  Rest of the capital: "+df.format(capitalRest)+"\n\n");
			
		}

		return ret;
	}
}
